package cs235.hash;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class HashSetCS235 extends NotImplemented {
    
    
    private int size;
    
    private static final int startingSize = 101;
    
    private LinkedHashSet<Object>[] buckets;
    
    private int currentMax = startingSize;
    
    @SuppressWarnings("unchecked")
    public HashSetCS235(){
        size = 0;
        buckets = new LinkedHashSet[currentMax];
    }
    

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size==0;
    }

    @Override
    public boolean contains(Object o) {
        if (o == null){
        return false;
        }
        int hash = getHashCode(o);
        if (buckets[hash] == null){
            return false;
        }
        return buckets[hash].contains(o);
        
    }

    @Override
    public Iterator<?> iterator() {
        return new SetIterator(toArray());
    }

    @Override
    public Object[] toArray() {
        @SuppressWarnings("unchecked")
        Set<Object>[] temp = new LinkedHashSet[buckets.length];
        System.arraycopy(buckets, 0, temp, 0, buckets.length);
        Object[] returnme = new Object[size];
        int position = 0;
        for (int i = 0; i<temp.length;i++){
            if (temp[i] != null){
                Object[] next = new Object[temp[i].size()];
                next = temp[i].toArray();
                for (int j=0;j<next.length;j++){
                    returnme[position++] = next[j]; 
                }
            }
        }
        return returnme;
    }


    @SuppressWarnings({
            "rawtypes", "unchecked"
    })
    @Override
    public boolean add(Object o) {
        if (o == null){
            return false;
        }

        if (contains(o)){
            return false;
        }
        
        if (size == currentMax){
            rehashSet();
        }
        
        int hash = getHashCode(o);
        
        if (buckets[hash]==null){
            buckets[hash] = new LinkedHashSet();
        }
        boolean added = buckets[hash].add(o);
        if (added){
            ++size;
        }
        return added;
    }
    
    
    
    @SuppressWarnings("unchecked")
    private void rehashSet(){
        Object[] temp = toArray();
        int tempSize = size;
        currentMax = 2 * currentMax + 1;
        buckets = new LinkedHashSet[currentMax];
        for (Object o : temp){
            add(o);
        }
        size = tempSize;
    }

    @Override
    public boolean remove(Object o) {
        if (o == null){
            return false;
        }
        int hash = getHashCode(o);
        if (buckets[hash]==null || !contains(o)){
            return false;
        }
        
        boolean removed = buckets[hash].remove(o); 
        if (removed){
            --size;
        }
        return removed;
    }
    

    @SuppressWarnings("unchecked")
    @Override
    public void clear() {
        for (int i=0;i<currentMax;i++){
            if (buckets[i] != null){
                buckets[i].clear();
            }
            buckets[i] = null;
        }
        size = 0;
        currentMax = startingSize;
        buckets = new LinkedHashSet[currentMax];
    }
    
    private int getHashCode(Object o){
        return Math.abs(o.hashCode() % currentMax);
    }
}
